package com.gitee.apanlh.util.net.ftp;

import com.gitee.apanlh.exp.FtpConnectionException;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.reflection.ClassConvertUtils;
import com.gitee.apanlh.util.valid.ValidParam;
import org.apache.commons.net.ftp.FTPClient;

import java.io.Closeable;

/**	
 * 	FTP连接客户端
 * 
 * 	@author Pan
 */
public class FtpConnection implements Closeable, FtpConfigureHandler {
	
	/** 匿名账户 */
	private static final String ANONYMOUS_ACCOUNT = "Anonymous";
	/** 默认端口 */ 
	private static final int DEFAULT_PORT = 21;
	
	/** IP */
	private String ip;
	/** 端口 */
	private int port;
	/** IP+PORT */
	private String addr;
	/** 用户名 */
	private String username;
	/** 密码 */
	private String password;
	/** 是否登录 */
	private boolean existLogin = false;
	/** 匿名登录 */
	private boolean anonymousLogin = false;
	/** 连接状态 */
	private boolean connected = false;
	/** 客户端 */
	private FTPClient client;
	/** 配置 */
	private FtpConfig config;
	/** 启动装配 */
	private FtpStarterConfigure starterConfig;
	/** 命令执行器 */ 
	private FtpCommandExecutor commandExecutors;
	
	/**	
	 * 	构造函数-默认端口，匿名登录，构建默认FTP配置
	 * 	
	 * 	@author Pan
	 * 	@param 	ip			地址
	 */
	public FtpConnection(String ip) {
		this(ip, DEFAULT_PORT);
	}
	
	/**	
	 * 	构造函数-匿名登录，构建默认FTP配置
	 * 	
	 * 	@author Pan
	 * 	@param 	ip			地址
	 * 	@param 	port		端口
	 */
	public FtpConnection(String ip, Object port) {
		this(ip, port, ANONYMOUS_ACCOUNT, null, null);
	}
	
	/**
	 * 	构造函数-用户无密码登录，构建默认FTP配置
	 * 	
	 * 	@author Pan
	 * 	@param 	ip			地址
	 * 	@param 	port		端口
	 * 	@param 	username	用户名
	 */
	public FtpConnection(String ip, Object port, String username) {
		this(ip, port, username, null, null);
	}
	
	/**
	 * 	构造函数-用户密码登录，构建默认FTP配置
	 * 	
	 * 	@author Pan
	 * 	@param 	ip			地址
	 * 	@param 	port		端口
	 * 	@param 	username	用户名
	 * 	@param 	password	密码
	 */
	public FtpConnection(String ip, Object port, String username, String password) {
		this(ip, port, username, password, null);
	}
	
	/**	
	 * 	构造函数-用户密码登录，自定义FTP配置
	 * 	
	 * 	@author Pan
	 * 	@param 	ip			地址
	 * 	@param 	port		端口
	 * 	@param 	username	用户名
	 * 	@param 	password	密码
	 * 	@param 	ftpConfig	FTP连接配置
	 */
	public FtpConnection(String ip, Object port, String username, String password, FtpConfig ftpConfig) {
		this.ip = ip;
		this.port = ClassConvertUtils.toInt(port);
		this.username = username;
		this.password = password;
		this.addr = StringUtils.format("ftp://{}:{}", this.ip, this.port);
		this.config = ftpConfig;
		//	匿名登录
		if (!ValidParam.isEmpty(username) && !ValidParam.isEmpty(password)) {
			this.anonymousLogin = false;
		}
	}
	
	/**
	 * 	初始化
	 * 	
	 * 	@author Pan
	 */
	void init() {
		if (connected) {
			close();
		}
		client = new FTPClient();
		//	设置默认配置
		if (config == null) {
			FtpConfig.getDefaultConfig(this);
		}
		if (starterConfig == null) {
			starterConfig = new FtpStarterConfigure(client, config);
		}
		//	创建命令执行器
		if (commandExecutors == null) {
			commandExecutors = new FtpCommandExecutor(client);
		}
	}
	
	/**	
	 * 	开启连接
	 * 	<br>返回执行器
	 * 	
	 * 	@author Pan
	 * 	@return	FtpCommandExecutor
	 */
	public FtpCommandExecutor connect() {
		try { 
			//	清理资源重置
			if (connected) {
				reset();
			}
			//	初始化
			init();
			beforeConfigure();
			
			FtpCommand command = commandExecutors.getCommand();
			
			if (StringUtils.eq(ANONYMOUS_ACCOUNT, username)) {
				anonymousLogin = true;
			}
			//	连接FTP
			client.connect(ip, port);
			setConnected(true);
			
			client.login(username, password);
			//	检测连接
			if (!command.isCompletion(true)) {
				throw new FtpConnectionException(command.getCommandDetails());
			}
			afterConfigure();
			setExistLogin(true);
		} catch (Exception e) {
			if (connected) {
				close();
			}
			throw new FtpConnectionException(StringUtils.format("ftp connect error, {}", e.getMessage()), e);
		}
		return commandExecutors;
	}
	
	/**
	 * 	如果已经开启状态则重置连接
	 * 	<br>关闭所有资源
	 * 	
	 * 	@author Pan
	 */
	private void reset() {
		close();
		client = null;
		config = null;
		starterConfig = null;
		commandExecutors = null;
		setExistLogin(false);
		setConnected(false);
		init();
	}
	
	/**	
	 * 	是否存在登录
	 * 	
	 * 	@author Pan
	 * 	@return	boolean
	 */
	public boolean getExistLogin() {
		return existLogin;
	}

	/**	
	 * 	获取IP
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public String getIp() {
		return ip;
	}

	/**	
	 * 	获取端口
	 * 	
	 * 	@author Pan
	 * 	@return	int
	 */
	public int getPort() {
		return port;
	}

	/**	
	 * 	获取连接地址信息
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public String getAddr() {
		return addr;
	}

	/**	
	 * 	获取连接用户名
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public String getUsername() {
		return username;
	}

	/**	
	 * 	获取连接密码
	 * 	
	 * 	@author Pan
	 * 	@return	String
	 */
	public String getPassword() {
		return password;
	}

	/**	
	 * 	获取FTP参数信息
	 * 	
	 * 	@author Pan
	 * 	@return	FtpConfig
	 */
	public FtpConfig getConfig() {
		return config;
	}

	/**	
	 * 	设置连接状态
	 * 	
	 * 	@author Pan
	 * 	@param 	connected	连接状态
	 */
	protected void setConnected(boolean connected) {
		this.connected = connected;
	}
	
	/**	
	 * 	设置登录状态
	 * 	
	 * 	@author Pan
	 * 	@param 	existLogin	登录
	 */
	protected void setExistLogin(boolean existLogin) {
		this.existLogin = existLogin;
	}
	
	/**	
	 * 	设置FTP参数配置
	 * 	
	 * 	@author Pan
	 * 	@param 	config	FTP配置类
	 */
	public void setConfig(FtpConfig config) {
		this.config = config;
	}

	/**	
	 * 	是否匿名登录
	 * 	
	 * 	@author Pan
	 * 	@return	boolean
	 */
	public boolean isAnonymousLogin() {
		return anonymousLogin;
	}

	/**	
	 * 	是否已经登陆连接成功
	 *  
	 * 	@author Pan
	 * 	@return	boolean
	 */
	public boolean isConnected() {
		return connected;
	}
	
	/**
	 * 	FTP退出并关闭
	 * 	
	 *	@author Pan
	 */
	@Override
	public void close() {
		if (client == null) {
			return ;
		}
		try {
			if (connected || client.isConnected()) {
				if (existLogin) {
					client.logout();
					setExistLogin(false);
				}
				client.disconnect();
				setConnected(false);
			}
		} catch (Exception e) {
			throw new FtpConnectionException(StringUtils.format("ftp close error, {}", e.getMessage()), e);
		}
	}

	@Override
	public void beforeConfigure() {
		starterConfig.beforeConfigure();
		commandExecutors.beforeConfigure();
	}

	@Override
	public void afterConfigure() {
		starterConfig.afterConfigure();
		commandExecutors.afterConfigure();
	}
}
